package org.tokaf.bestcolumnfinder;

import org.tokaf.TopKElement;
import org.tokaf.algorithm.Algorithm;
import org.tokaf.datasearcher.DataSearcher;
import org.tokaf.rater.Rater;

/**
 * <p>Each column has same weight, as is the derivation of threshold in
 * aggregating function in that column.</p> <p>For aggregation function x+2y,
 * first column has weight 1 and second 2. Number of rows read in column is
 * proportional to its weight.</p> <p>Copyright (c) 2006</p>
 * @author Alan Eckhardt
 * @version 1.0
 */
public class DerivationInPointFinder implements BestColumnFinder {

	protected int count[];

	protected double derivations[];

	protected DataSearcher[] myData;

	protected SimpleColumnFinder scf = new SimpleColumnFinder();

	protected int lastColumn;

	protected TopKElement last[];

	public DerivationInPointFinder(DataSearcher[] data, Rater rater) {
		count = new int[data.length];
		derivations = new double[data.length];
		last = new TopKElement[data.length];
		myData = data;
		for (int i = 0; i < data.length; i++) {
			count[i] = 0;
			derivations[i] = 0;
			last[i] = null;
		}
	}

	public double[] getDerivations() {
		return derivations;
	}

	/**
	 * Computes derivations in all columns, computing the derivation in current
	 * element in each column.
	 * @param alg
	 * @return false, if all columns are empty or all derivations are zero.
	 *         True, if derivations were computed correctly.
	 */
	protected boolean computeDerivationsInData(Algorithm alg) {
		boolean end = true;
		Rater rater = alg.getRater();
		DataSearcher[] data = alg.getDataSearchers();
		// if (nextValue(data) == -1)
		for (int i = 0; i < myData.length; i++) {
			if (!myData[i].hasNext())
				continue;

			TopKElement el = alg.findEntity(last[i]);
			if (el == null) {
				throw new NullPointerException();
			}
			// actualDerivations[i] = rater.getDerivation(i, data, values);
			// if (actualDerivations[i] != 0)
			derivations[i] = rater.getDerivation(i, data, el);
			if (derivations[i] != 0)
				end = false;
		}
		return end;
	}

	/**
	 * Computes derivations in all columns, computing the derivation in
	 * threshold.
	 * @param alg
	 * @return false, if all columns are empty or all derivations are zero.
	 *         True, if derivations were computed correctly.
	 */
	protected boolean computeDerivationsInThreshold(Algorithm alg) {
		boolean end = true;
		Rater rater = alg.getRater();
		DataSearcher[] data = alg.getDataSearchers();
		TopKElement threshold = alg.getThreshold();
		// if (nextValue(data) == -1)
		for (int i = 0; i < myData.length; i++) {
			if (!myData[i].hasNext())
				continue;

			if (threshold == null) {
				throw new NullPointerException();
			}
			derivations[i] = rater.getDerivation(i, data, threshold);
			if (derivations[i] != 0)
				end = false;
		}
		return end;
	}

	protected boolean computeDerivations(Algorithm alg) {
		return computeDerivationsInThreshold(alg);
	}

	/**
	 * Finds the highest derivation among columns that are not empty.
	 * @param data
	 * @return index of highest derivation.
	 */
	protected int nextValue(DataSearcher[] data) {
		int max = -1;
		for (int i = 0; i < derivations.length; i++)
			if (!data[i].hasNext())
				continue;
			else if (max == -1
					|| (count[i] / derivations[i] < count[max]
							/ derivations[max]))
				max = i;
		return max;
	}

	/**
	 * Checks, whether some double in array is not zero.
	 * @param last
	 * @return true, if all numbers are zero. False, if at least one is
	 *         non-zero.
	 */
	protected boolean checkZero(double[] last) {
		for (int i = 0; i < last.length; i++)
			if (last[i] != 0)
				return false;
		return true;
	}

	/**
	 * Checks, if any datasearcher has some elements, or any of given numbers is
	 * non-zero.
	 * @param data
	 * @param last
	 * @return
	 */
	protected boolean checkFinish(DataSearcher[] data, double[] last) {
		if (checkZero(last) == true)
			return true;

		for (int i = 0; i < data.length; i++)
			if (data[i].hasNext())
				return false;

		return true;
	}

	public int findColumn(Algorithm alg) {

		DataSearcher[] data = alg.getDataSearchers();

		TopKElement threshold = alg.getThreshold();
		if (threshold == null) {
			threshold = new TopKElement("", -1, data.length);
			for (int i = 0; i < data.length; i++)
				threshold.setRating(i, data[i].getField(2), data[i]);
		}

		boolean end = true;
		try {
			end = computeDerivations(alg);
		} catch (NullPointerException ex) {
			lastColumn = scf.findColumn(alg);

			if (lastColumn != -1) {
				count[lastColumn]++;
				last[lastColumn] = new TopKElement(data[lastColumn].getField(0)
						.toString());
			}
			return lastColumn;
		}

		if (end) {
			lastColumn = scf.findColumn(alg);
			if (lastColumn != -1) {
				count[lastColumn]++;
				last[lastColumn] = new TopKElement(data[lastColumn].getField(0)
						.toString());
			}
			return lastColumn;
		}

		lastColumn = nextValue(data);
		if (lastColumn != -1) {
			count[lastColumn]++;
			last[lastColumn] = new TopKElement(data[lastColumn].getField(0)
					.toString());
		}
		return lastColumn;
	}
}
